package com.sso.gateway.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * Desc:
 *
 * @author: keen
 * Date: 2020-06-26
 * Time: 16:05
 */
@Component
public class JwtFilter2 extends ZuulFilter {

    private final static String SIGN_IN_URL = "/auth/signIn";
    private final static String AUTHORIZATION_PREFIX = "Bearer ";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public String filterType() {
        /**
         pre：请求在被路由之前执行

         routing：在路由请求时调用

         post：在routing和errror过滤器之后调用

         error：处理请求时发生错误调用

         */
        return "pre";
    }

    @Override
    public int filterOrder() {
        return 0;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        if(request.getRequestURI().contains(SIGN_IN_URL)){
            return null;
        }
        String uid = getJtiFromCookie(request);
        if (StringUtils.isBlank(uid)) {
            accessDenied(context);
            return null;
        }
        String token;
        try {
            token = getTokenFromCache(uid);
        } catch (IOException e) {
            throw new ZuulException(e, 500, e.getMessage());
        }
        if (StringUtils.isBlank(token)) {
            accessDenied(context);
            return null;
        }
        if (!checkToken(request, token)) {
            accessDenied(context);
            return null;
        }
        return null;
    }

    private String getJtiFromCookie(HttpServletRequest request) {
        String uid = "";
        Cookie[] cookies = request.getCookies();
        if (ArrayUtils.isNotEmpty(cookies)) {
            Cookie cookie = Arrays.stream(cookies)
                    .filter(c -> "uid".equals(c.getName()))
                    .findFirst()
                    .orElse(null);
            if (cookie != null) {
                uid = cookie.getValue();
            }
        }
        return uid;
    }


    private String getTokenFromCache(String uid) throws IOException {
        String token = "";
        String tokenKey = "user_token:" + uid;
        String jsonToken = stringRedisTemplate.opsForValue().get(tokenKey);
        if (StringUtils.isNotBlank(jsonToken)) {
            Map tokenMap = objectMapper.readValue(jsonToken, Map.class);
            token = (String) tokenMap.get("access_token");
        }
        return token;
    }

    private boolean checkToken(HttpServletRequest request, String token) {
        String authorization = request.getHeader("Authorization");
        if (StringUtils.isBlank(authorization)) {
            return false;
        }
        if (!authorization.startsWith(AUTHORIZATION_PREFIX)) {
            return false;
        }
        return token.equals(StringUtils.substringAfter(authorization, AUTHORIZATION_PREFIX));
    }


    private void accessDenied(RequestContext requestContext) {
        requestContext.setSendZuulResponse(false);
        requestContext.setResponseStatusCode(200);
        HttpServletResponse response = requestContext.getResponse();
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        requestContext.setResponse(response);
        requestContext.setResponseBody("请登录");
    }
}
